#!/usr/bin/python

from veil import extractARPDstMac
import socket, struct, sys, time, random, pickle, os

# Local imports 
from veil import * # for the constants.
from threading import Thread
from mx.DateTime.DateTime import Friday

#######################################
#    Server Port THREAD FUNCTION
#######################################
def serverThread(server_socket):
    while(True):
        print '-----------------------------------------------------'
        client_socket, address = server_socket.accept()
        print "\n",myprintid,"Received an incoming connection from ", address
        packet = receivePacket(client_socket) # added by Hon
        if len(packet) != 0:
            print 'Received packet: ',packet.encode("hex")
            if len(packet) < HEADER_LEN + 8:
                print 'Malformed packet! Packet Length:',len(packet), 'Expected:',HEADER_LEN+8
                client_socket.close()
                continue
            printPacket(packet,L)
            processPacket(packet)
        else:
            print '[FastRRDebug] NHA test received'
        client_socket.close()
###############################################
#    Server THREAD FUNCTION ENDS HERE
###############################################

#######################################
#    failureThread THREAD FUNCTION
#######################################
def failureThread(ftime):
    pid = os.getpid()
    print '[NodeFailure] Node:', myvid, 'will fail after', ftime,' seconds and pid is ',pid
    time.sleep(ftime)
    print '[NodeFailure] Node:', myvid, 'is about to fail!!!!'
    #The README said there are exceptions that need to be handled, not sure what exceptions tho
    server_socket.close()
    os.system('kill -9 '+str(pid))	
    print '[NodeFailure] Am I:', myvid, 'still alive?'
###############################################
#    failureThread THREAD FUNCTION ENDS HERE
###############################################

#######################################
#    killme function definition
#######################################
def killme(ftime, pid, server_socket):
    print '[Die] Dieing in ',ftime, 'secs'
    time.sleep(ftime)
    server_socket.close()		
    print '[Die] Dieing ################################################################################################DIEING NOWWWWWWWWWWWWWWWW##################### '
    os.system('kill -9 '+str(pid))
    print '[Die] Am I:', myvid, 'still alive?'
#############################################
#    killme function definition ENDS HERE
#############################################

# Adds an entry to rdvStore, and also ensures that there are no duplicates
def addIfNODuplicateRDVENTRY(dist,newentry):
    global rdvStore
    for x in rdvStore[dist]:
        if x[0] == newentry[0] and x[1] == newentry[1]:
            return
    rdvStore[dist].append(newentry)
    
# Finds a logically closest gateway for a given svid    
def findAGW(rdvStore,k,svid):    
    gw = {}
    if k not in rdvStore:
        return ''
    for t in rdvStore[k]:
        r = delta(t[0],svid)
        if r not in gw:
            gw[r] = t[0]
        gw[r] = t[0]  
    if len(gw) == 0:
        return ''
    print "GATEWAYS FOUND: " + str(gw)
    s = gw.keys()
    s.sort()
    if(len(s)>=3):
        return [gw[s[0]], gw[s[1]], gw[s[2]]]
    elif(len(s)>=2):
        return [gw[s[0]], gw[s[1]]]
    else:
        return [gw[s[0]]]


def reduceTTL(packet):
    TTL = int(bin2str((struct.unpack("!B", packet[20:21]))[0],L),2)
    if TTL <= 1:
        print "################################################################### TTL EXPIRED!!" 
        return None
    addInPacketCount(TTL)
    TTL = TTL - 1
    print "################################################################### NEW TTL: " + str(TTL)
    svid = bin2str((struct.unpack("!I", packet[8:12]))[0],L)
    dvid = bin2str((struct.unpack("!I", packet[12:16]))[0],L)
    fdvid = bin2str((struct.unpack("!I", packet[16:20]))[0],L)
    pkt = struct.pack('!HHBBH', HTYPE, PTYPE, HLEN, PLEN, 0x0000)\
            + struct.pack('!I', int(svid, 2))\
            + struct.pack('!I', int(dvid,2))\
            + struct.pack('!I', int(fdvid,2))\
            + struct.pack('!B', TTL)\
            + struct.pack('!BHI', 0x00, 0x0000, 0x00000000)
    return pkt
    
######################################
#    checkIfNhopAlive start here
######################################
def checkIfNhopAlive(nhop):
    toNhop = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    ##socket.setdefaulttimeout(1.0)
    toNhop.settimeout(10)
    address = nhop.split(':')
    ##toNhop.connect((address[0],int(address[1])))
    result = toNhop.connect_ex((address[0],int(address[1])))
    toNhop.shutdown(socket.SHUT_RDWR)
    toNhop.close()
    if result == 0:
        print "[FastRRDebug]Next hop alive: TURE"
        return 1
    else:
        print "[FastRRDebug]Next hop alive: FALSE"
        return 0	
    	
######################################
#    checkIfNhopAlive ENDS HERE
######################################

######################################
#    handleNhopFailures STARTS HERE
######################################
def handleNhopFailures(src, dest, packet, case):
    #case 1 where forwarding directive == myvid
    if case == 1:
        print "#############################################################INSIDE HANDLER CASE 1 #####################"
        alive = 0
        nhop = "notNone"
        while alive != 1 and nhop != None:
            nhop = selectNLH(dest, src)
            if nhop != None:
                alive = checkIfNhopAlive(nhop)
                if alive == 0:
                    rmNhopFromAll(nhop)
        return [nhop,packet]
    elif case == 2:
    # case 2 forwarding directive is my physical neighbor and it is down
        print "#############################################################INSIDE HANDLER CASE 2 #####################"
        bucketDistance = delta(myvid,dest)        
        alive = 0
        nhop = "notNone"
        while alive != 1 and nhop != None:
            if bucketDistance in routingTable:
                gws = routingTable[bucketDistance]
                if len(gws)>1:
                    closestGW = getClosestGateway(dest, gws)
                    nhop = bin2str(closestGW[0],L)
                    nhop = vid2pid[nhop]                
                else:
                    nhop = None
                    return [nhop,packet]
                alive = checkIfNhopAlive(nhop)
                if alive == 0:
                    rmNhopFromAll(nhop)
            else:
                nhop = None
        if nhop != None: #if we found a gateway with working next hop
            gwvid = bin2str(closestGW[1],L)
            if gwvid == myvid:
                packet = setForwardingDirective(dest, packet)
                print "#############################################################INSIDE HANDLER SET NEW FORWARDING DIRECTIVE TO",getForwardingDirective(packet, L)
            else:
                packet = setForwardingDirective(gwvid,packet)
                print "#############################################################INSIDE HANDLER SET NEW FORWARDING DIRECTIVE TO",getForwardingDirective(packet, L)
        return [nhop,packet]
    elif case == 3:
        #case 3 repeatedly call selectNLH for working next hop
        print "#############################################################INSIDE HANDLER CASE 3 #####################"
        alive = 0
        nhop = "notNone"
        while alive != 1 and nhop != None:
            nhop = selectNLH(dest, src)
            if nhop != None:
                alive = checkIfNhopAlive(nhop)
                if alive == 0:
                    rmNhopFromAll(nhop)
        return [nhop,packet]
    elif case == 4:
        #repeatedly look up routing table using the top  gateway until
        print "#############################################################INSIDE HANDLER CASE 4 #####################"
        bucketDistance = delta(myvid,dest)  
        alive = 0
        nhop = "notNone"
        while alive != 1 and nhop != None:
            if bucketDistance in routingTable:
                nhop = bin2str(routingTable[bucketDistance][0][0],L)
                nhop = vid2pid[nhop]
                alive = checkIfNhopAlive(nhop)
                if alive == 0:
                    rmNhopFromAll(nhop)
            else:
                nhope = None               
        return [nhop,packet]
    elif case == 5:
        # case 5 forwarding directive == dest, randomly chose a gateway, but the next hop is down
        bucketDistance = delta(myvid,dest)
        print "#############################################################INSIDE HANDLER CASE 5 #####################"        
        alive = 0
        nhop = "notNone"
        while alive != 1 and nhop != None:
            if bucketDistance in routingTable:
                gws = routingTable[bucketDistance]
                if len(gws) > 1:
                    randomGW = chooseRandomGateway(dest, gws)
                else:
                    randomGW = gws[0]
                nhop = bin2str(randomGW[0],L)
                nhop = vid2pid[nhop]               
                alive = checkIfNhopAlive(nhop)
                if alive == 0:
                    rmNhopFromAll(nhop)
            else:
                nhop = None
        if nhop != None: #if we found a gateway with working next hop
            gwvid = bin2str(randomGW[1],L)
            if gwvid == myvid:
                packet = setForwardingDirective(dest, packet)
            else:
                packet = setForwardingDirective(gwvid,packet)
        return [nhop,packet]
######################################
#    handleNhopFailures ENDS HERE
######################################        
                  

######################################
#    rmNhopFromAll start here
######################################
#If you pass a nhop that has already been deleted, you screwing up
#don't do it!
def rmNhopFromAll(nhop):
    global pid2vid_d, vid2pid_d, routingTable 	
    nhop_vid = pid2vid[nhop]
    print 'From rmNhopFromAll print DynamicCopy at', myvid, ':', pid2vid_d
    print 'From rmNhopFromAll print DynamicCopy at', myvid, ':', vid2pid_d
    #Deleting the neighbor info from the dynamnic dictionaries
    #and leaving the original unchanged, this is for the routeDataPacket function
    #to not break
    #try:
    del pid2vid_d[nhop]
    #except KeyError:
        #print 'KeyError exception happened at', myvid, ', nhop:', nhop, ';pid2vid_d:', pid2vid_d
    #try:
    del vid2pid_d[nhop_vid]
    #except KeyError:
        #print 'KeyError exception happened at',myvid,', nhop_vid:', nhop_vid, ';vid2pid_d:', vid2pid_d
    print '[FastRRDebug]To remove:', nhop_vid
    vlen = len(myvid)
    print '[FastRRDebug] Routing table before removing', nhop_vid
    printRoutingTable()
    
    for i in range(1,vlen+1):
        if i in routingTable:
            routingTable[i]= [j for j in routingTable[i] if bin2str(j[0],L) != nhop_vid]
            if len(routingTable[i]) == 0:
                del routingTable[i]        
 
    print '[FastRRDebug] Removed all entries with', nhop_vid
    
    print '[FastRRDebug] New routing table looks like this'
    printRoutingTable()
        
######################################
#    rmNhopFromAll ENDS HERE
######################################

######################################
#    printRoutingTable start here
######################################

def printRoutingTable(): 
    vlen = len(myvid)
    print 'Printing Routing table at node:', myvid
    for i in range(1,vlen+1):
        if i in routingTable:
            for j in routingTable[i]:
                print 'Bucket #', i, 'Next hop:',bin2str(j[0],L), 'Gateway:',bin2str(j[1],L), 'Prefix:',j[2], 'Default:', j[3]
        else:
            print 'Bucket #',i,'  --- E M P T Y --- ' 
        
######################################
#    printRoutingTable ENDS HERE
######################################

#######################################
#    PROCESSPACKET FUNCTION
#######################################
def processPacket(packet):
    dst = getDest(packet,L)
    packettype = getOperation(packet)
    
    # forward the packet if I am not the destination
    if dst != myvid:
        if packettype == DATA:
            packet = reduceTTL(packet)
            if packet == None:
                addDropPacket()
                return
        routepacket(packet)
        return
    
    # I am the destination of the packet, so process it.
    print myprintid, 'Processing packet'
    printPacket(packet,L)
    svid = bin2str((struct.unpack("!I", packet[8:12]))[0],L)
    payload = bin2str((struct.unpack("!I", packet[16:20]))[0],L)
    
    if packettype == DATA:
        print myprintid, "############################################## RECEIVED DATA PACKET!"
        addOutPacketCount()
        
    elif packettype == RDV_PUBLISH:
        dist = delta(myvid,payload)
        if dist not in rdvStore:
            rdvStore[dist] = []
        newentry = [svid,payload]
        addIfNODuplicateRDVENTRY(dist,newentry)
        return
        
    elif packettype == RDV_QUERY:
        k = int(payload,2)
        # search in rdv store for the logically closest gateway to reach kth distance away neighbor
        gw = findAGW(rdvStore,k,svid)
        # if found then form the reply packet and send to svid
        if len(gw) == 0:
            # No gateway found
            print myprintid, 'No gateway found for the rdv_query packet to reach bucket: ',k,' for node: ', svid
            return
        # create a RDV_REPLY packet and send it
        replypacket = createRDV_REPLY(gw,k,myvid, svid)
        addRDVPacketCount()
        #replypacket = createRDV_REPLY([int(gw,2), int(gw,2)],k,myvid, svid) # -Anshuman
        routepacket(replypacket)
        return
    elif packettype == RDV_REPLY:
        print "PACKET LENGTH: " + str(len(packet))
        # Fill my routing table using this new information
        gw = []
        if(len(packet) == 32):
            gw.append(struct.unpack("!I", packet[20:24]))
            gw.append(struct.unpack("!I", packet[24:28]))
            gw.append(struct.unpack("!I", packet[28:32]))
        if(len(packet) == 28):
            gw.append(struct.unpack("!I", packet[20:24]))
            gw.append(struct.unpack("!I", packet[24:28]))
        if(len(packet) == 24):
            gw.append(struct.unpack("!I", packet[20:24]))
        
        k = int(payload,2)
        #The following 2 lines are moved to be contained with the nested if below
        #if k not in routingTable:
           #routingTable[k] = [] 
        for i in range(0,len(gw)):
            [tuplegw] = gw[i]
            gw_str = bin2str(tuplegw,L)
            if gw_str == myvid:
                #print '################################################################### PHYSICAL LINK ADVERTIZED AS GATEWAY:',gw_str
                continue
            nexthop = getNextHop(gw_str)
            if nexthop == '':
                print 'ERROR: no nexthop found for the gateway:',gw_str
                print 'New routing information couldnt be added! '
                return
            nh = int(pid2vid[nexthop],2)
            default = "Yes" if i == 0 else "No";
            bucket_info = [nh, tuplegw, getPrefix(myvid,k), default]
            if nexthop in pid2vid_d: #added by Hon
                if k not in routingTable:
                    routingTable[k] = []
                if not isDuplicateBucket(routingTable[k], bucket_info):                
                    routingTable[k].append(bucket_info)
        #if k in routingTable:
        #    print myprintid, 'Already have an entry to reach neighbors at distance: ',k
        #    return
    else:
        print myprintid, 'Unexpected Packet!!'
###############################################
#    SPROCESSPACKET FUNCTION ENDS HERE
###############################################
###############################################
#    getNextHop function starts here
###############################################

def getNextHop(destvid_str):
    nexthop = ''
    if destvid_str in vid2pid:
        return vid2pid[destvid_str]
    dist = delta(myvid,destvid_str)
    if dist in routingTable:
        nexthop = bin2str(routingTable[dist][0][0],L)
        nexthop = vid2pid[nexthop]
    return nexthop
    
###############################################
#    getNextHop FUNCTION ENDS HERE
###############################################

###############################################
#    sendPacket function starts here
###############################################

def sendPacket(packet,nexthop):
    # connect to the nexthop
    try:
        toSwitch = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        address = nexthop.split(':')
        toSwitch.connect((address[0],int(address[1])))
        toSwitch.send(packet)
        toSwitch.close()
    except:
        print myprintid,"Unexpected Exception: ",'received while sending packet to Switch at IP: ', address[0], 'port: ',address[1]
        printPacket(packet,L)
        packettype = getOperation(packet)
        if packettype == DATA:
            addDropPacket()
    # send the packet
    
    # close the connection
    
###############################################
#    sendPacket FUNCTION ENDS HERE
###############################################

def routeControlPacket(packet):
    global myvid, routingTable, vid2pid, myprintid, L
    src = getSource(packet, L)
    
    dst = getDest(packet,L)
    # If it's me
    if dst == myvid:
        #print 'I am the destination!'
        processPacket(packet)
        return
    
    #If it's my physical Neighbor
    if dst in vid2pid:
        print "################################################################### CONTROL PACKET IS DESTINED TO MY PHYSICAL NEIGHBOR!"
        if dst in vid2pid_d:
            nexthop = vid2pid[dst]
            #alive = checkIfNhopAlive(nexthop)

            alive=1
            if alive == 1:
                sendPacket(packet,nexthop)
                print "################################################################### PACKET SENT TO", nexthop
                return
            else:
                print "################################################################### PHYSICAL NEIGHBOR (DEST) IS DOWN!"
                rmNhopFromAll(nexthop)
        print "################################################################### PHYSICAL NEIGHBOR (DEST) IS DOWN!"
        #print '[FastRRDebug] Destination is down, drop packet'
        return
            
    
    nexthop = None
    packettype = getOperation(packet)
    while nexthop == None:
        if dst in vid2pid:
            if dst in vid2pid_d:
                nexthop = vid2pid[dst]
                alive=1 #alive = checkIfNhopAlive(nexthop)
                if alive == 0:
                    rmNhopFromAll(nexthop)
                    nexthop = None
            else:
                nexthop = None
            break
        dist = delta(myvid,dst)
        if dist == 0:
            break
        if dist in routingTable:
            nexthop = bin2str(routingTable[dist][0][0],L)
            nexthop = vid2pid[nexthop]
            alive = 1 #alive = checkIfNhopAlive(nexthop)
            if alive == 0:
                rmNhopFromAll(nexthop)
                handlerReturn = handleNhopFailures(src,dst,packet,4)
                nexthop = handlerReturn[0]        
                packet = handlerReturn[1]
            break
        if (packettype != RDV_PUBLISH) and (packettype != RDV_QUERY):
            break 
        
        print myprintid,'No next hop for destination: ',dst,'dist: ', dist
        print 'nexthop value = ', nexthop
        # flip the dist bit to
        print 'Changing dest by fliping bit'
        dst = flipBit(dst,dist)
         
    #print 'MyVID: ', myvid, 'DEST: ', dst
    if dst != getDest(packet,L):
        # update the destination on the packet
        packet = updateDestination(packet,dst)
    if dst == myvid:
        #print "I am the destination for this RDV Q/P message:"
        printPacket(packet,L)
        processPacket(packet)
    if nexthop == None:
        print myprintid,'no route to destination' ,'MyVID: ', myvid, 'DEST: ', dst
        printPacket(packet,L)
        #print 'Control Packet dropped'
        return
    sendPacket(packet,nexthop)
    print "################################################################### PACKET SENT TO", nexthop
    return


def routeDataPacket(packet):
    print "################################################################### INSIDE ROUTE DATA PACKET"
    global myvid, routingTable, vid2pid, myprintid, L
    
    #get source
    src = getSource(packet, L)
    #get destination
    dst = getDest(packet,L)
    print "################################################################### DESTINATION: " + dst
    #get forwarding directive
    fdirective = getForwardingDirective(packet, L)
    print "################################################################### FORWARDING DIRECTIVE: " + fdirective
    
    # If it's me
    if dst == myvid:
        print "################################################################### I AM THE DESTINATION!!!"
        processPacket(packet)
        return
    
    if dst in vid2pid:
        print "################################################################### DESTINATION IS MY PHYSICAL NEIGHBOR!!!"
        # Check whether this neighbor had been deleted before, coz it was down
        if dst in vid2pid_d:
            nexthop = vid2pid[dst]
            alive = checkIfNhopAlive(nexthop)
            if alive == 1:
                sendPacket(packet,nexthop)
                print "################################################################### PACKET SENT TO", nexthop
                return
            else:
                #print "################################################################### DESTINATION IS DOWN, DROPPING THE PACKET!!!"
                rmNhopFromAll(nexthop)               
                # 2 is the case number which is used to differentiate between different
                # areas of this function, depending on how we got nhop
                # this case will modify forwarding directive
                addDropPacket()
                return
        else:
            #print "################################################################### DESTINATION IS DOWN, DROPPING THE PACKET!!!"
            addDropPacket()
            return
        
    if fdirective == myvid:
        print "################################################################### I AM THE FORWARDING DIRECTIVE!!!"
        #I was the intended gateway, change the fdirective to the destination and forward to a gateway on the next level
        #destination and gateway can not be the same
        #find appropriate next level gateway
        print "################################################################### CHANGING THE FORWARDING DIRECTIVE TO: " + str(dst)
        packet = setForwardingDirective(dst, packet)
        nexthop = selectNLH(dst, src)
        print "################################################################### FOUND NEXTHOP: " + str(nexthop)
        alive = checkIfNhopAlive(nexthop)
        if alive == 1:
            sendPacket(packet, nexthop)
            print "################################################################### PACKET SENT TO", nexthop
            return
        ####THIS HAS TO BE CHANGED##############		
        else:
            rmNhopFromAll(nexthop)            
            # 1 is the case number which is used to diffrentiate between different
            # areas of this function, depending on how we got nhop
            handlerReturn = handleNhopFailures(src, dst, packet, 1)
            nexthop = handlerReturn[0]
            packet = handlerReturn[1]
            if nexthop != None:
                sendPacket(packet, nexthop)
                print "################################################################### PACKET SENT TO", nexthop
                return
            #print "############################################################## All PHYSICAL NEIGHBORS ARE DOWN PACKET DROPPED ################################"
	          addDropPacket()
            return
								
    #Check if forwarding directive is my physical Neighbor
    if fdirective in vid2pid:
        print "################################################################### FORWARDING DIRECTIVE IS MY PHYSICAL NEIGHBOR!!!"
        # Check whether this neighbor had been deleted before, coz it was down
        if fdirective in vid2pid_d:
            nexthop = vid2pid[fdirective]
            alive = checkIfNhopAlive(nexthop)
            if alive == 1:
                sendPacket(packet,nexthop)
                print "################################################################### PACKET SENT TO", nexthop
                return
            else:
                print "################################################################### FORWARDING DIRECTIVE IS DOWN!!!"
                rmNhopFromAll(nexthop)               
                # 2 is the case number which is used to differentiate between different
                # areas of this function, depending on how we got nhop
                # this case will modify forwarding directive
                handlerReturn = handleNhopFailures(src, dst, packet, 2)
                nexthop = handlerReturn[0]
                packet = handlerReturn[1]
                if nexthop != None:
                    print "###################################################################SET NEW FORWARDING DIRECTIVE TO",getForwardingDirective(packet, L)
                    sendPacket(packet, nexthop)
                    print "################################################################### PACKET SENT TO", nexthop
                    printPacket(packet,L)
                    return
                #print "######################## PACKET DROPPED, OUT OF GATEWAYS GOING TO BUCKET#:",delta(myvid,dst), "################################"
	              addDropPacket()
                return
        print "################################################################### FORWARDING DIRECTIVE IS DOWN!!!"
        handlerReturn = handleNhopFailures(src, dst, packet, 2)
        nexthop = handlerReturn[0]        
        packet = handlerReturn[1]
        if nexthop != None:
            print "###################################################################SET NEW FORWARDING DIRECTIVE TO",getForwardingDirective(packet, L)
            sendPacket(packet, nexthop)
            print "################################################################### PACKET SENT TO", nexthop
            printPacket(packet,L)
            return
        #print "######################## PACKET DROPPED, OUT OF GATEWAYS GOING TO BUCKET#:",delta(myvid,dst), "################################"
	      addDropPacket()
        return
    
    nexthop = None
    #packettype = getOperation(packet) -- Anshuman
    dist = delta(myvid,fdirective)
    if dist in routingTable:
        print "################################################################### DISTANCE TO FORWARDING DIRECTIVE: " + str(dist)
        possibleGWs = routingTable[dist]
        print "################################################################### POSSIBLE GATEWAYS: " + str(possibleGWs)
        #check if forwarding directive and destination are the same
        #if they are then assume that you are the source and choose new gateway and change the forwarding directive to it
        if fdirective == dst:
            print "################################################################### FVID AND DVID ARE THE SAME!"
            if len(possibleGWs) > 1:
                randomGW = chooseRandomGateway(dst, possibleGWs)
            else:
                randomGW = possibleGWs[0]
                #except IndexError:
                    #print 'IndexError: length-', len(possibleGWs), 'pGWs:', possibleGWs    
            print "################################################################### SETTING FVID TO: " + str(randomGW[1])
            fvid = bin2str(randomGW[1], L)
            if fvid == myvid:
                print "################################################################### NOW I AM THE FORWARDING DIRECTIVE!!!"
                print "################################################################### CHANGING THE FORWARDING DIRECTIVE TO: " + str(dst)
                packet = setForwardingDirective(dst, packet)
                nexthop = selectNLH(dst, src)
                alive = checkIfNhopAlive(nexthop)
                if alive == 0:
                    rmNhopFromAll(nexthop)
                    # Case 3 of handleNhopFailures would repeatedly call selectNLH and search for a working next hop
                    handlerReturn = handleNhopFailures(src, dst,packet,3)
                    nexthop = handlerReturn[0]        
                    packet = handlerReturn[1]
            else:
                packet = setForwardingDirective(fvid, packet)
                nexthop = bin2str(randomGW[0],L)
                nexthop = vid2pid[nexthop]
                alive = checkIfNhopAlive(nexthop)
                if alive == 0:
                    rmNhopFromAll(nexthop)
                    # Case 5 of handleNhopFailures would repeatedly call chooseRandowGateway and search for a gateway with a working next hop
                    # this call will modify forwarding directive
                    handlerReturn = handleNhopFailures(src, dst,packet,5)
                    nexthop = handlerReturn[0]        
                    packet = handlerReturn[1]
        else:
						#If forwarding directive is not the destination
            print "################################################################### SIMPLY FORWARDING, LOOKING FOR A CLOSEST GATEWAY"
            #Get the closest gateway to the forwarding directive
            closestGW = getClosestGateway(fdirective, possibleGWs)
            print "################################################################### USING GATEWAY: " + str(closestGW[1])
            nexthop = bin2str(closestGW[0],L)
            nexthop = vid2pid[nexthop]
            alive = checkIfNhopAlive(nexthop)
            if alive == 0:
                rmNhopFromAll(nexthop)
                handlerReturn = handleNhopFailures(src, dst,packet,2)
                nexthop = handlerReturn[0]        
                packet = handlerReturn[1]
        ##Choosing the best gateway!
        #nexthop = bin2str(possibleGWs[0][0],L)
        #nexthop = vid2pid[nexthop]
        
    print "################################################################### FOUND NEXTHOP: " + str(nexthop)    
    if nexthop == None:
        print myprintid,'No next hop for destination: ',fdirective,' dist: ', dist
        print myprintid,'No route to destination' ,'MyVID: ', myvid, 'Forwarding Directive: ',fdirective
        #print myprintid,"################################################################### PACKET DROPPED!" 
        addDropPacket()
        return
    sendPacket(packet,nexthop)
    print "################################################################### PACKET SENT TO", nexthop
    return
    
###############################################
#    routepacket function starts here
###############################################

def chooseRandomGateway(dst, gateways):
    gatewayToChoose = random.randint(0, len(gateways)-1)
    return gateways[gatewayToChoose]

def getClosestGateway(fvid, gateways):
    nextGateway = None
    lowestDist = 999
    for gateway in gateways:
        dist = delta(fvid, bin2str(gateway[1],L))
        if(dist < lowestDist):
            nextGateway = gateway
            lowestDist = dist
    return nextGateway
            
def selectNLH(dst, src):
    possibleGWs = {}
    #find k level gateway in vid2pid table
    nextHop = None
    lowestDist = 999
    for vid in vid2pid_d:
        if vid == src:
            continue
        dist = delta(dst,vid)
        if(dist < lowestDist):
            nextHop = vid2pid_d[vid]
            lowestDist = dist
    return nextHop

def setForwardingDirective(forwardingDirective, packet):
    TTL = int(bin2str((struct.unpack("!B", packet[20:21]))[0],L),2)
    svid = bin2str((struct.unpack("!I", packet[8:12]))[0],L)
    dvid = bin2str((struct.unpack("!I", packet[12:16]))[0],L)
    fdvid = forwardingDirective
    pkt = struct.pack('!HHBBH', HTYPE, PTYPE, HLEN, PLEN, 0x0000)\
            + struct.pack('!I', int(svid, 2))\
            + struct.pack('!I', int(dvid,2))\
            + struct.pack('!I', int(fdvid, 2))\
            + struct.pack('!B', TTL)\
            + struct.pack('!BHI', 0x00, 0x0000, 0x00000000)
    return pkt


def routepacket(packet):
    packettype = getOperation(packet)
    if (packettype == DATA):
        routeDataPacket(packet)
        return
    routeControlPacket(packet)

###############################################
#    routepacket FUNCTION ENDS HERE
###############################################
###############################################
#    Publish function starts here
###############################################

def publish(bucket,k):
    global myvid
    dst = getRendezvousID(k,myvid)
    packet = createRDV_PUBLISH(bucket,myvid,dst)
    addRDVPacketCount()
    print myprintid, 'Publishing my neighbor', bin2str(bucket[0],L), 'to rdv:',dst
    printPacket(packet,L)
    routepacket(packet)

###############################################
#    Publish FUNCTION ENDS HERE
###############################################

###############################################
#    Query function starts here
###############################################

def query(k):
    global myvid
    dst = getRendezvousID(k,myvid)
    packet = createRDV_QUERY(k,myvid,dst)
    addRDVPacketCount()
    print myprintid, 'Quering to reach Bucket:',k, 'to rdv:',dst
    printPacket(packet,L)
    routepacket(packet)

###############################################
#    Query FUNCTION ENDS HERE
###############################################

###############################################
#    Utility functions for perf. analysis
###############################################

def addInPacketCount(TTL):
    global perf, myvid
    addPacketCount()
    if TTL == 64:
        perf[INPACKETS] += 1 
        print "###################################################### PACKET ENTRY POINT"
        print "###################################################### PACKET NO AT NODE:" +str(myvid) + " = " + str(perf[INPACKETS])
	FILE =  open( PERF_DIR + "output." +str(myvid) + ".p", "wb" )
        pickle.dump(perf, FILE)
	FILE.close()


def addOutPacketCount():
    global perf, myvid
    addPacketCount()
    perf[OUTPACKETS] += 1 
    print "###################################################### PACKET END POINT"
    print "###################################################### PACKET NO AT NODE:" +str(myvid) + " = " + str(perf[OUTPACKETS])
    FILE =  open( PERF_DIR + "output." +str(myvid) + ".p", "wb" )
    pickle.dump(perf, FILE)
    FILE.close()

def addPacketCount():
    global perf
    perf[PACKETS] += 1
    print "###################################################### PACKET ROUTE POINT"
    print "###################################################### PACKET NO AT NODE:" +str(myvid) + " = " + str(perf[PACKETS])
    FILE =  open( PERF_DIR + "output." +str(myvid) + ".p", "wb" )
    pickle.dump(perf, FILE)
    FILE.close()

def addRDVPacketCount():
    global perf
    perf[RDVPACKETS] += 1
    print "###################################################### RDV PACKET SENT"
    print "###################################################### PACKET NO AT NODE:" +str(myvid) + " = " + str(perf[RDVPACKETS])
    FILE =  open( PERF_DIR + "output." +str(myvid) + ".p", "wb" )
    pickle.dump(perf, FILE)
    FILE.close()

def addDropPacket():
    global perf
    perf[DROPPACKETS] += 1
    #print "###################################################### PACKET DROPPED"
    print "###################################################### PACKET NO AT NODE:" +str(myvid) + " = " + str(perf[DROPPACKETS])
    FILE =  open( PERF_DIR + "output." +str(myvid) + ".p", "wb" )
    pickle.dump(perf, FILE)
    FILE.close()
    

###############################################
#    RunARount function starts here
###############################################

def runARound(round):
    global routingTable
    global vid2pid, pid2vid, mypid, myvid,L
    # start from round 2 since connectivity in round 1 is already learnt using the physical neighbors
    for i in range(2,round+1):
        # see if routing entry for this round is already available in the routing table.
        if i in routingTable:
            if len(routingTable[i]) > 0:
                #publish the information if it is already there
                for t in routingTable[i]:
                    if t[1] == int(myvid,2):
                        publish(t,i)
            else:
                query(i)
   #     else:  - Anshuman
        query(i)

###############################################
#    RunARound FUNCTION ENDS HERE
###############################################

if len(sys.argv) != 5:
    print '-----------------------------------------------'
    print 'Wrong number of input parameters'
    print 'Usage: ', sys.argv[0], ' <TopologyFile>', '<vid_file>', '<my_ip:my_port>', '<fail time in seconds>'
    print 'A node will have two identifiers'
    print 'i) pid: in this project pid is mapped to IP:Port of the host so if a veil_switch is running at flute.cs.umn.edu at port 5211 than pid of this switch is = flute.cs.umn.edu:5211'
    print 'ii) vid: It is the virtual id of the switch.'
    print 'TopologyFile: It contains the adjacency list using the pids. So each line contains more than one pid(s), it is interepreted as physical neighbors of the first pid.'
    print 'vid_file: It contains the pid to vid mapping, each line here contains a two tuples (space separated) first tuple is the pid and second tuple is the corresponding vid'
    print '-----------------------------------------------\n\n\n'
    sys.exit(0)

sleeptime = random.random()*5
print 'Sleeping :',sleeptime,' seconds!'
time.sleep(sleeptime)
topofile = sys.argv[1]
vidfile = sys.argv[2]
myport = int((sys.argv[3].split(":"))[1])
mypid = sys.argv[3]
failtime = int(sys.argv[4])


# Learn my neighbors by reading the input adjacency list file
myneighbors = []
myvid = ''
pid2vid = {}
vid2pid = {}
#The _d vars hold the dynamnically changing neighbor information
#based on if the neighbor is down, we make changes to this dictionary
#and leave the original neighbor information in pid2vid and vid2pid resp.
pid2vid_d = {}
vid2pid_d = {}
routingTable = {} 
rdvStore = {} 
myprintid = ''
perf = {INPACKETS: 0, OUTPACKETS: 0, PACKETS: 0, RDVPACKETS: 0, DROPPACKETS: 0}
L = 0
default = ''
# Routing table is a dictionary, it contains the values at each distances from 1 to L
# So key in the routing table is the bucket distance, value is the 3 tuple: tuple 1 = nexthop (vid), tuple 2 = gateway (vid), tuple 3 = prefix (string)

# RDV STORE is a dictionary: it stores the list of edges with distances
# open the topo file
fin = open(topofile,'r')
# read the file line by line
line = fin.readline()
line = line.strip()
while line != '':
    if line.find(mypid) ==0:
        # this is the line which contains the neighbor list for my 
        myneighbors = (line.split(' '))[1:]
        break
    line = fin.readline()
    line = line.strip()
fin.close()

if ' ' in myneighbors:
    print 'Warning: My neighbor list contains empty pids!'
if  len(myneighbors) < 1:
    print 'Warning: My neighbor list is empty, will quit now!'
    sys.exit(0)
print 'Myneighbors: ',myneighbors    
# Learn my and myneighbor's vids
fin = open(vidfile, 'r')
line = fin.readline()
line = line.strip()
while line != '':
    tokens = line.split(' ')
    if tokens[0] in myneighbors:
        pid2vid[tokens[0]] = tokens[1]
        #pid2vid_d[tokens[0]] = tokens[1]
        vid2pid[tokens[1]] = tokens[0] 
        #vid2pid_d[tokens[1]] = tokens[0] 
    elif tokens[0] == mypid:
        myvid = tokens[1] 
    
    line = fin.readline()
    line = line.strip()
fin.close()
pid2vid_d = pid2vid.copy()
print 'print DynamicCopy:', pid2vid_d
vid2pid_d = vid2pid.copy()
print 'print DynamicCopy:', vid2pid_d

# Learn L, it is the length of any vid
L = len(myvid)
myprintid = "VEIL_SWITCH: ["+mypid+'|'+myvid+']'
# Now start my serversocket to listen to the incoming packets         
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("", myport))
server_socket.listen(5)
print myprintid, ' is now listening at port: ',myport

# create a server thread to take care of incoming messages:
server = Thread(target=serverThread,args=([server_socket]))
server.start()

# initialize perf file with empty values
pickle.dump(perf , open( PERF_DIR + "output." +str(myvid) + ".p", "wb" ) )

# if user entered non-zero value for fail time, then kill node after 'failtime' seconds
if failtime > 0:
    failure = Thread(target=failureThread,args=([failtime]))
    failure.start()

round = 1
# Put my physical neighnor information in the routing table in the round 1
for vid in vid2pid:
    dist = delta(vid,myvid)
    if dist not in routingTable:
        routingTable[dist] = []
        default = "Yes"
    bucket_len = len(routingTable[dist])
    bucket_info = [int(vid,2), int(myvid,2), getPrefix(myvid,dist), default]
    if not isDuplicateBucket(routingTable[dist], bucket_info):
        routingTable[dist].append(bucket_info)
        
while True:
    print myprintid, 'Starting Round #',round
    runARound(round)
    round = round + 1
    if round > L:
        round = L
    print '\n\t----> Routing Table at :',myvid,'|',mypid,' <----'
    for i in range(1,L+1):
        if i in routingTable:
            for j in routingTable[i]:
                print 'Bucket #', i, 'Nexthop:',bin2str(j[0],L), 'Gateway:',bin2str(j[1],L), 'Prefix:',j[2], 'Default:', j[3]
        else:
            print 'Bucket #',i,'  --- E M P T Y --- '
    print 'RDV STORE: ', rdvStore
    print '\n --  --  --  --  -- --  --  --  --  -- --  --  --  --  -- \n'
    time.sleep(ROUND_TIME)
